package my.suveng.oauth2.service.user.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.caller.CallerUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.log.Log;
import cn.hutool.log.LogFactory;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.querydsl.jpa.impl.JPAQueryFactory;
import my.suveng.model.common.interfaces.response.IMessage;
import my.suveng.model.common.response.Message;
import my.suveng.model.common.response.element.ElementPage;
import my.suveng.model.common.response.element.PageQuery;
import my.suveng.oauth2.model.dto.*;
import my.suveng.oauth2.model.struct.SysGroupStructMapper;
import my.suveng.oauth2.model.struct.SysPermissionStructMapper;
import my.suveng.oauth2.model.struct.SysRoleStructMapper;
import my.suveng.oauth2.model.struct.SysUserStructMapper;
import my.suveng.oauth2.service.auth.impl.AuthServiceImpl;
import my.suveng.oauth2.service.db.mysql.dao.jpa.*;
import my.suveng.oauth2.service.db.mysql.model.entity.*;
import my.suveng.oauth2.service.user.IUserService;
import my.suveng.oauth2.vender.NetEase.email.NetEaseEmailServiceImpl;
import my.suveng.util.log.PlusLogFactoryHutool;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Example;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.*;

@Service
public class UserServiceImpl implements IUserService {

    // hutool日志
    private static final Log log = LogFactory.setCurrentLogFactory(new PlusLogFactoryHutool())
            .getLog(CallerUtil.getCaller().getName());
    private static final String VERIFY_CODE_PRE = "verify:code:";

    @Autowired
    private SysUserRepository sysUserRepository;
    @Autowired
    private SysPermissionRepository sysPermissionRepository;
    @Autowired
    private SysPermissionRoleRepository sysPermissionRoleRepository;
    @Autowired
    private SysUserRoleRepository sysUserRoleRepository;
    @Autowired
    private SysUserGroupRepository sysUserGroupRepository;
    @Autowired
    private SysGroupRoleRepository sysGroupRoleRepository;
    @Autowired
    private SysRoleRepository sysRoleRepository;
    @Autowired
    private PasswordEncoder passwordEncoder;
    @Autowired
    private SysGroupRepository sysGroupRepository;
    @Autowired
    private AuthServiceImpl authService;
    @Autowired
    private JPAQueryFactory jpaQueryFactory;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private NetEaseEmailServiceImpl emailService;

    /**
     * 获取oauth的user信息
     *
     * @author suwenguang
     */
    public User getUserInfoByUsername(String username) {
        if (StrUtil.isBlank(username)) {
            return null;
        }

        SysUser sysUser = sysUserRepository.findByUsername(username);
        if (ObjectUtil.isEmpty(sysUser)) {
            log.error("数据库查询的user信息为空");
            return null;
        }

        log.info("查询到用户信息:{}", JSON.toJSONString(sysUser));

        // authorities权限信息
        ArrayList<GrantedAuthority> authorities = getAuthoritiesByUserId(sysUser.getId());

        // user密码信息
        String password = sysUser.getPassword();
        if (StrUtil.isBlank(password)) {
            log.error("用户密码信息为空");
            return null;
        }

        return new User(username, password, authorities);
    }

    /**
     * 根据userId 获取权限
     *
     * @author suwenguang
     */
    private ArrayList<GrantedAuthority> getAuthoritiesByUserId(long userId) {
        ArrayList<GrantedAuthority> result = new ArrayList<>();
        HashSet<String> authoritiesSet = new HashSet<>();

        // 获取组的角色权限 一级权限
        List<SysUserGroup> groups = sysUserGroupRepository.findAllByUserId(userId);
        if (CollUtil.isNotEmpty(groups)) {
            groups.parallelStream().forEach(e -> {
                List<SysGroupRole> groupRoles = sysGroupRoleRepository.findAllByGroupId(e.getGroupId());
                if (CollUtil.isNotEmpty(groupRoles)) {
                    groupRoles.parallelStream().forEach(g -> {
                        List<SysPermissionRole> rolePermissions = sysPermissionRoleRepository
                                .findAllByRoleId(g.getRoleId());
                        getAuthoritySet(authoritiesSet, rolePermissions);
                    });
                }
            });
        }

        // 获取用户独有角色权限 二级权限
        List<SysUserRole> userRoles = sysUserRoleRepository.findAllByUserId(userId);
        if (CollUtil.isNotEmpty(userRoles)) {
            userRoles.parallelStream().forEach(e -> {
                List<SysPermissionRole> rolePermissions = sysPermissionRoleRepository.findAllByRoleId(e.getRoleId());
                getAuthoritySet(authoritiesSet, rolePermissions);
            });
        }

        if (CollUtil.isNotEmpty(authoritiesSet)) {
            authoritiesSet.parallelStream().forEach(e -> {
                SimpleGrantedAuthority authority = new SimpleGrantedAuthority(e);
                result.add(authority);
            });
        }

        return result;
    }

    /**
     * 获取权限字符串
     *
     * @author suwenguang
     */
    private void getAuthoritySet(HashSet<String> authoritiesSet, List<SysPermissionRole> rolePermissions) {
        if (CollUtil.isEmpty(rolePermissions)) {
            return;
        }

        rolePermissions.parallelStream().forEach(r -> {
            Optional<SysPermission> optionalPermission = sysPermissionRepository.findById(r.getPermissionId());
            if (optionalPermission.isPresent()) {
                SysPermission permission = optionalPermission.get();
                String permissionName = permission.getName();
                if (StrUtil.isNotBlank(permissionName)) {
                    authoritiesSet.add(permissionName);
                }
            }
        });
    }

    /**
     * 增加权限
     *
     * @author suwenguang
     */
    public boolean addPermission(PermissionAddDto permissionAddDto) {
        SysPermission sysPermission = SysPermissionStructMapper.INSTANCE.dto2entity(permissionAddDto);

        SysPermission record = sysPermissionRepository.findByName(permissionAddDto.getPermissionName());
        if (ObjectUtil.isNotEmpty(record)) {
            log.error("权限已存在");
            return true;
        }

        SysPermission save = sysPermissionRepository.save(sysPermission);
        return ObjectUtil.isNotEmpty(save) && ObjectUtil.isNotEmpty(save.getId());
    }

    /**
     * 增加/修改 角色
     *
     * @author suwenguang
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public IMessage<SysRole> addRole(RoleAddDto roleAddDto) {
        SysRole sysRole = SysRoleStructMapper.INSTANCE.dto2entity(roleAddDto);
        // 创建者信息
        SysUser sysUser = authService.getSysUser();
        if (ObjectUtil.isNotEmpty(sysRole)) {
            sysRole.setCreator(sysUser.getId());
            sysRole.setUpdator(sysRole.getId());
        }

        // 角色-权限关系
        List<Long> permissions = roleAddDto.getPermissions();
        if (ObjectUtil.isEmpty(permissions)) {
            log.info("没有权限列表参数");
            permissions = new ArrayList<>();
        }

        LinkedHashSet<Long> permissionSet = new LinkedHashSet<>(permissions.size());
        permissionSet.addAll(permissions);

        // 创建角色
        SysRole result = sysRoleRepository.findByName(sysRole.getName());
        if (ObjectUtil.isEmpty(result)) {
            log.error("角色不存在,创建角色");
            SysRole save = sysRoleRepository.save(sysRole);
            result = save;
            if (!(ObjectUtil.isNotEmpty(save) && ObjectUtil.isNotEmpty(save.getId()))) {
                log.error("角色创建失败,record={}", JSON.toJSONString(save));
                return Message.error("角色创建失败");
            }
        }

        // 创建权限与角色关联关系
        SysRole finalRole = result;
        permissionSet.forEach(e -> {
            // 保证权限是存在的,可能存在 可重复读问题
            Optional<SysPermission> optionalSysPermission = sysPermissionRepository.findById(e);
            if (!optionalSysPermission.isPresent()) {
                return;
            }

            SysPermissionRole sysPermissionRole = new SysPermissionRole();
            sysPermissionRole.setRoleId(finalRole.getId());
            sysPermissionRole.setPermissionId(e);
            SysPermissionRole prResult = sysPermissionRoleRepository.save(sysPermissionRole);
            if (ObjectUtil.isEmpty(prResult) || ObjectUtil.isEmpty(prResult.getId())) {
                log.error("权限与角色关系创建失败,record={}", sysPermissionRole);
                throw new RuntimeException("权限与角色关系创建失败");
            }
        });

        return Message.successWithData(result);
    }

    /**
     * 保存组对象
     *
     * @author suwenguang
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public IMessage<SysGroup> addGroup(GroupAddDto dto) {
        SysGroup newGroup = SysGroupStructMapper.INSTANCE.dto2entity(dto);
        if (ObjectUtil.isEmpty(newGroup)) {
            log.info("转化对象失败,对象为空");
            return Message.error("转化对象失败,对象为空");
        }

        // 查找数据库是否有组
        SysGroup probe = new SysGroup();
        probe.setName(newGroup.getName());
        Optional<SysGroup> one = sysGroupRepository.findOne(Example.of(probe));
        if (one.isPresent()) {
            log.error("组名称已存在,更新失败");
            return Message.error("组名称已存在,更新失败");
        }

        SysGroup result = sysGroupRepository.save(newGroup);
        if (ObjectUtil.isEmpty(result.getId())) {
            log.error("组更新失败,recor{}", JSON.toJSONString(newGroup));
            return Message.error("组更新失败");
        }

        List<Long> roleIds = dto.getRoleIds();
        if (CollUtil.isEmpty(roleIds)) {
            log.info("没有role_id");
            return Message.error("没有role_id");
        }
        // 查找数据库是否有组与角色的关系
        roleIds.forEach(e -> {
            // role不存在跳过
            Optional<SysRole> roleById = sysRoleRepository.findById(e);
            if (!roleById.isPresent()) {
                log.info("role不存在,跳过,record={}", JSON.toJSONString(e));
                return;
            }

            SysGroupRole sysGroupRoleExample = new SysGroupRole();
            sysGroupRoleExample.setRoleId(e);
            sysGroupRoleExample.setGroupId(result.getId());
            Optional<SysGroupRole> optionalSysGroupRole = sysGroupRoleRepository
                    .findOne(Example.of(sysGroupRoleExample));
            if (!optionalSysGroupRole.isPresent()) {
                SysGroupRole sysGroupRole = sysGroupRoleRepository.save(sysGroupRoleExample);
                if (ObjectUtil.isEmpty(sysGroupRole.getId())) {
                    log.error("增加组与角色失败,record={}", JSON.toJSONString(sysGroupRoleExample));
                    throw new RuntimeException("增加组与角色失败");
                }
            }
        });

        return Message.successWithData(result);
    }

    /**
     * 查询用户列表 多条件搜索
     *
     * @author suwenguang
     */
    public IMessage<ElementPage<List<SysUser>>> userList(PageQuery<UserSearch> query) {
        Specification<SysUser> specification = buildSpecification(query);
        Sort.Order order = query.order();

        Page<SysUser> list;
        Specification<SysUser> where = Specification.where(specification);

        if (ObjectUtil.isEmpty(order)) {
            list = sysUserRepository.findAll(where, PageRequest.of(query.getCurrentPage() - 1, query.getPageSize()));
        } else {
            Sort sort = Sort.by(order);
            list = sysUserRepository.findAll(where,
                    PageRequest.of(query.getCurrentPage() - 1, query.getPageSize(), sort));
        }

        return ElementPage.build(list.getTotalElements(), list.getContent());
    }

    /**
     * 动态构造查询条件
     *
     * @author suwenguang
     */
    private Specification<SysUser> buildSpecification(PageQuery<UserSearch> query) {
        if (ObjectUtil.isEmpty(query) || ObjectUtil.isEmpty(query.getSearch())) {
            return null;
        }
        UserSearch search = query.getSearch();

        return new Specification<SysUser>() {
            @Override
            public Predicate toPredicate(Root<SysUser> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                ArrayList<Predicate> list = new ArrayList<>();

                if (ObjectUtil.isNotEmpty(search.getId())) {
                    list.add(criteriaBuilder.equal(root.get("id").as(Long.class), search.getId()));
                }

                // name模糊查询
                if (StrUtil.isNotBlank(search.getUsername())) {
                    list.add(criteriaBuilder.like(root.get("username").as(String.class),
                            "%" + search.getUsername() + "%"));
                }

                // phone模糊查询
                if (StrUtil.isNotBlank(search.getPhone())) {
                    list.add(criteriaBuilder.like(root.get("phone").as(String.class), "%" + search.getPhone() + "%"));
                }

                // email模糊查询
                if (StrUtil.isNotBlank(search.getEmail())) {
                    list.add(criteriaBuilder.like(root.get("email").as(String.class), "%" + search.getEmail() + "%"));
                }

                if (ObjectUtil.isNotEmpty(search.getCreator())) {
                    list.add(criteriaBuilder.equal(root.get("creator").as(Long.class), search.getCreator()));
                }

                if (ObjectUtil.isNotEmpty(search.getUpdator())) {
                    list.add(criteriaBuilder.equal(root.get("updator").as(Long.class), search.getUpdator()));
                }

                if (ObjectUtil.isNotEmpty(search.getStatus())) {
                    list.add(criteriaBuilder.equal(root.get("status").as(Integer.class), search.getStatus()));
                }

                // 时间
                if (StrUtil.isNotBlank(search.getCreateTimeStart())) {
                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("createTime").as(String.class),
                            search.getCreateTimeStart()));

                }
                if (StrUtil.isNotBlank(search.getCreateTimeEnd())) {
                    list.add(criteriaBuilder.lessThan(root.get("createTime").as(String.class),
                            search.getCreateTimeEnd()));
                }

                // 时间
                if (StrUtil.isNotBlank(search.getModifyTimeStart())) {
                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get("modifyTime").as(String.class),
                            search.getModifyTimeStart()));
                }
                if (StrUtil.isNotBlank(search.getModifyTimeEnd())) {
                    list.add(criteriaBuilder.lessThan(root.get("modifyTime").as(String.class),
                            search.getModifyTimeEnd()));
                }

                Predicate[] p = new Predicate[list.size()];

                return criteriaBuilder.and(list.toArray(p));
            }
        };
    }

    /**
     * 修改用户信息
     *
     * @author suwenguang
     */
    public IMessage<Object> edit(UserEditDto userEditDto) {
        // 限制
        QSysUser qSysUser = QSysUser.sysUser;
        SysUser oldUsername = jpaQueryFactory.selectFrom(qSysUser)
                .where(qSysUser.username.eq(userEditDto.getUsername())).fetchOne();
        if (ObjectUtil.isNotEmpty(oldUsername)) {
            log.info("username已存在,record={}", JSON.toJSONString(oldUsername));
            return Message.error("username已存在");
        }
        // 查询旧记录
        SysUser currUser = authService.getSysUser();
        currUser.setUsername(userEditDto.getUsername());
        // 更新
        SysUser newUser = sysUserRepository.save(currUser);

        return Message.success();
    }

    /**
     * 批量授予用户角色
     *
     * @author suwenguang
     */
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public IMessage<List<SysUserRole>> addRoles(UserAddRoleDto userAddRoleDto) {
        ArrayList<SysUserRole> result = new ArrayList<>();

        List<Long> roleIds = userAddRoleDto.getRoleIds();
        if (CollUtil.isNotEmpty(roleIds)) {
            for (Long roleId : roleIds) {
                IMessage<SysUserRole> sysUserRoleMessage = addRole(userAddRoleDto.getId(), roleId);
                if (!sysUserRoleMessage.isSuccess()) {
                    throw new RuntimeException("事务异常");
                }
                result.add(sysUserRoleMessage.getData());
            }
        }
        return Message.successWithData(result);
    }

    /**
     * 授予用户角色
     *
     * @author suwenguang
     */
    public IMessage<SysUserRole> addRole(Long userId, Long roleId) {
        // 检查user
        QSysUser qSysUser = QSysUser.sysUser;
        SysUser sysUser = jpaQueryFactory.selectFrom(qSysUser).where(qSysUser.id.eq(userId)).fetchOne();
        if (ObjectUtil.isEmpty(sysUser)) {
            log.info("用户不存在,userId={}", userId);
            return Message.error("用户不存在");
        }
        // 检查role
        QSysRole qSysRole = QSysRole.sysRole;
        SysRole sysRole = jpaQueryFactory.selectFrom(qSysRole).where(qSysRole.id.eq(roleId)).fetchOne();
        if (ObjectUtil.isEmpty(sysRole)) {
            log.info("角色信息不存在,roleId={}", roleId);
            return Message.error("角色信息不存在");
        }
        // 插入关系
        SysUserRole entity = new SysUserRole();
        entity.setUserId(userId);
        entity.setRoleId(roleId);

        SysUser user = authService.getSysUser();
        if (ObjectUtil.isEmpty(user)) {
            log.info("token用户不存在");
            return Message.error("token用户不存在");
        }
        entity.setCreator(user.getId());
        entity.setUpdator(user.getId());
        entity.setCreateTime(new Date());
        entity.setModifyTime(new Date());

        SysUserRole save = sysUserRoleRepository.save(entity);
        if (ObjectUtil.isEmpty(save.getId())) {
            log.info("user-role入库失败,record={}", JSON.toJSONString(save));
            return Message.error("permission入库失败");
        }

        return Message.successWithData(save);
    }

    /**
     * 发送验证码
     *
     * @author suwenguang
     */
    public IMessage<Object> sendEmailVerfiyCode(String email) {
        Assert.notNull(email, "不能为空");

        // 保存验证码到redis
        String code = RandomUtil.randomNumbers(4);
        redisTemplate.opsForValue().set(VERIFY_CODE_PRE + email, code);
        // 发送
        emailService.sendAsync("suveng@qq.com", "系统验证码", "验证码: " + code);
        return Message.success();
    }

    /**
     * 注册
     *
     * @author suwenguang
     */
    public IMessage<Object> register(RegisterDto dto) {
        // 获取验证码,比对
        String key = VERIFY_CODE_PRE + dto.getEmail();
        String redisCode = redisTemplate.opsForValue().get(key);
        if (StrUtil.isBlank(redisCode)) {
            log.info("redis的code为空,key={}", key);
            return Message.error("请重新发送验证码");
        }

        if (!redisCode.equals(dto.getCode())) {
            log.info("验证码不匹配,source={},target={}", dto.getCode(), redisCode);
            return Message.error("验证码不正确");
        }

        if (!dto.getPassword().equals(dto.getRepeatPassword())) {
            log.info("重复密码不一致,pass={},repeat={}", dto.getPassword(), dto.getRepeatPassword());
            return Message.error("重复密码不一致");
        }

        // 保存用户
        UserAddDto userAdd = new UserAddDto();
        userAdd.setUsername(dto.getEmail());
        userAdd.setPassword(dto.getPassword());
        userAdd.setRoleIds(Lists.newArrayList(1L));
        IMessage<SysUser> sysUserMessage = addUser(userAdd);
        if (sysUserMessage.isSuccess()) {
            return Message.successWithData(sysUserMessage.getData());
        }

        return Message.error(sysUserMessage.getMessage());
    }

    /**
     * 增加用户
     *
     * @author suwenguang
     */
    @Transactional(rollbackFor = Exception.class)
    public IMessage<SysUser> addUser(UserAddDto dto) {

        SysUser newUser = SysUserStructMapper.INSTANCE.dto2entity(dto);
        if (ObjectUtil.isEmpty(newUser)) {
            log.info("转化对象失败");
            return Message.error("转化对象失败");
        }
        newUser.setPassword(passwordEncoder.encode(newUser.getPassword()));

        SysUser probe = new SysUser();
        probe.setUsername(newUser.getUsername());
        Optional<SysUser> optionalSysUser = sysUserRepository.findOne(Example.of(probe));
        if (optionalSysUser.isPresent()) {
            log.error("用户名已存在,new={},record={}", JSON.toJSONString(newUser), JSON.toJSONString(optionalSysUser.get()));
            return Message.error("用户名已存在");
        }

        SysUser result = sysUserRepository.save(newUser);
        if (ObjectUtil.isEmpty(result.getId())) {
            log.error("用户入库失败,record={}", JSON.toJSONString(newUser));
            return Message.error("用户入库失败");
        }

        List<Long> groupIds = dto.getGroupIds();
        if (CollUtil.isNotEmpty(groupIds)) {
            groupIds.forEach(e -> {
                Optional<SysGroup> groupById = sysGroupRepository.findById(e);
                if (!groupById.isPresent()) {
                    log.info("group查询失败,没有该id:{}", e);
                    return;
                }

                SysUserGroup sysUserGroupExample = new SysUserGroup();
                sysUserGroupExample.setGroupId(e);
                sysUserGroupExample.setUserId(result.getId());
                Optional<SysUserGroup> optionalSysUserGroup = sysUserGroupRepository
                        .findOne(Example.of(sysUserGroupExample));
                if (optionalSysUserGroup.isPresent()) {
                    return;
                }

                SysUserGroup saveUserGroup = sysUserGroupRepository.save(sysUserGroupExample);
                if (ObjectUtil.isEmpty(saveUserGroup.getId())) {
                    log.error("用户与组的关系入库失败,record={}", JSON.toJSONString(saveUserGroup));
                    throw new RuntimeException("用户与组的关系入库失败");
                }
            });
        }

        List<Long> roleIds = dto.getRoleIds();
        if (CollUtil.isNotEmpty(roleIds)) {
            roleIds.forEach(e -> {
                Optional<SysRole> optionalSysRole = sysRoleRepository.findById(e);
                if (!optionalSysRole.isPresent()) {
                    log.info("role查询失败,没有该id:{}", e);
                    return;
                }

                SysUserRole userRoleExample = new SysUserRole();
                userRoleExample.setUserId(result.getId());
                userRoleExample.setRoleId(e);

                Optional<SysUserRole> optionalSysUserRole = sysUserRoleRepository.findOne(Example.of(userRoleExample));
                if (optionalSysUserRole.isPresent()) {
                    log.info("关系已存在,reocrd={}", JSON.toJSONString(optionalSysUserRole));
                    return;
                }

                SysUserRole userRoleSave = sysUserRoleRepository.save(userRoleExample);
                if (ObjectUtil.isEmpty(userRoleSave.getId())) {
                    log.error("用户与角色入库失败,record={}", JSON.toJSONString(userRoleExample));
                    throw new RuntimeException("用户与角色入库失败");
                }
            });
        }

        return Message.successWithData(result);
    }

}
